/*

 Copyright 2012 Peter Dornbach.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
   
*/

package com.dornbachs.zebra.guitarear;

import java.lang.reflect.Field;
import java.util.Vector;

import android.app.ProgressDialog;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class SoundPlayer {
	public class Instrument {
		public Instrument(String name) {
			_name = name;
		}

		public void addSample(int note, int soundId) {
			int idx = noteIndex(note);
			_notes.add(idx, note);
			_sampleIds.add(idx, soundId);
		}

		public void play(String note) {
		  play(ChordShape.toMidiNote(note));
		}

		public void play(int note) {
			int idx = lowerNoteIndex(note);
			int diff = note - _notes.get(idx);
			float rate = RATES[diff + RATES_BASE];
			int stream = _pool.play(_sampleIds.get(idx), 1.0f, 1.0f, 0, 0, rate);
			
			_playingStreams[_playingStreamIdx] = stream;
			_playingStreamIdx = _playingStreamIdx + 1;
			if (_playingStreamIdx >= MAX_STREAMS) {
				_playingStreamIdx = 0;
			}
		}

		public void mute() {
			for (int stream: _playingStreams) {
				_pool.stop(stream);
			}
		}

		public void setVolume(float volume) {
			for (int stream: _playingStreams) {
				_pool.setVolume(stream, volume, volume);
			}
		}

		public String getName() {
			return _name;
		}

		private int noteIndex(int note) {
			int idx = 0;
			while (idx < _notes.size() && _notes.get(idx) < note)
				idx++;
			return idx;
		}

		private int lowerNoteIndex(int note) {
			int idx = noteIndex(note);
			return (idx == 0 ? 0 : idx - 1);
		}

		private String _name;
		private Vector<Integer> _notes = new Vector<Integer>();
		private Vector<Integer> _sampleIds = new Vector<Integer>();
	}

	public Instrument getInstrument(String name) {
		for (int i = 0; i < _instruments.size(); ++i) {
			if (_instruments.get(i).getName().equals(name)) {
				return _instruments.get(i);
			}
		}
		return null;
	}

	public void init(Context context) {
		class Init implements Runnable {
			public Init(SoundPlayer player, Context context) {
				_player = player;
				_context = context;

				_progress = new ProgressDialog(context);
				_progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
				String s = context.getString(R.string.progress_init_text);
				_progress.setMessage(s);
				_progress.setCancelable(false);
				_progress.show();
			}

			public void run() {
				_player.initInBackground(_context, _progress.getMax(), _handler);
			}

			private Handler _handler = new Handler() {
				@Override
				public void handleMessage(Message m) {
					switch (m.what) {
					case MESSAGE_PROGRESS:
						_progress.setProgress(m.arg1);
						break;
					case MESSAGE_DONE:
						_progress.hide();
						break;
					}
				}
			};

			private Context _context;
			private SoundPlayer _player;
			private ProgressDialog _progress;
		}
		;

		new Thread(new Init(this, context)).start();
	}

	public void release() {
		_pool.release();
		_pool = null;
	}

	private void initInBackground(Context context, int progressMax,
			Handler handler) {
		_pool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);

		Field[] samples = R.raw.class.getDeclaredFields();
		int numSamples = 0;
		for (Field sample : samples) {
			if (sample.getName().startsWith(SAMPLE_PREFIX)) {
				numSamples++;
			}
		}

		int numInitializedSamples = 0;
		for (Field sample : samples) {
			String name = sample.getName();
			if (name.startsWith(SAMPLE_PREFIX)) {
				try {
					int resourceId = sample.getInt(null);
					String[] split = name.split("_");
					String instrName = split[1];
					Instrument instr = getInstrument(instrName);
					if (instr == null) {
						instr = new Instrument(instrName);
						_instruments.add(instr);
					}

					int note = ChordShape.toMidiNote(split[2]);
					int sampleId = _pool.load(context, resourceId, 1);
					instr.addSample(note, sampleId);
					numInitializedSamples++;
					handler.sendMessage(Message.obtain(handler, MESSAGE_PROGRESS,
							numInitializedSamples * progressMax / numSamples, 0));
				} catch (IllegalAccessException e) {
					Log.e("zebra", e.toString());
				}
			}
		}

		handler.sendMessage(Message.obtain(null, MESSAGE_DONE));
	}

	private static final int MESSAGE_PROGRESS = 1;
	private static final int MESSAGE_DONE = 2;

	private static final float HALFNOTE_RATE = (float) Math.pow(2.0, 1.0 / 12);

	private static final int RATES_BASE = 12;
	private static final float RATES[];

	private static final String SAMPLE_PREFIX = "sample_";

	private static final int MAX_STREAMS = 6;

	static {
		final int RATES_MAX = 36;
		RATES = new float[RATES_MAX];
		for (int i = 0; i < RATES_MAX; ++i) {
			RATES[i] = (float) Math.pow(HALFNOTE_RATE, i - RATES_BASE);
		}
	}

	private SoundPool _pool;
	private Vector<Instrument> _instruments = new Vector<Instrument>();

	private int _playingStreams[] = new int[MAX_STREAMS];
	private int _playingStreamIdx;
}
